using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Drawing;
using System.Xml;
using System.IO;
using System.ComponentModel;

using LogAnalyzer;
using LogAnalyzer.Grabbers;
using LogAnalyzer.Dialogs;

namespace LogAnalyzer.Controls
{
    public class Viewer : Control
    {
        private int _maxVisibleLinesNumber = 1000;

        private ViewerRichTextBox _rtb;
        private List<DisplayFilter> _displayFilters;
        private SingleViewer _viewerContainer;
        private bool _follow = true;
        private bool _viewLastLine = true;
        private bool _disposing = false;


        string _buffer = "";
        object _syncAppendingText = new object();


        #region Context Menu

        ContextMenuStrip contextMenu = new ContextMenuStrip();
        Controls.LocalizedToolStripMenuItem toolSaveCurrentLog = new Controls.LocalizedToolStripMenuItem();
        Controls.LocalizedToolStripMenuItem toolCopyToClipboard = new Controls.LocalizedToolStripMenuItem();
        Controls.LocalizedToolStripMenuItem toolClearAll = new Controls.LocalizedToolStripMenuItem();
        Controls.LocalizedToolStripMenuItem toolClearBeforeLine = new Controls.LocalizedToolStripMenuItem();
        Controls.LocalizedToolStripMenuItem toolViewLastLine = new Controls.LocalizedToolStripMenuItem();
        Controls.LocalizedToolStripMenuItem toolGrabberFilters = new Controls.LocalizedToolStripMenuItem();
        Controls.LocalizedToolStripMenuItem toolDisplayFilters = new Controls.LocalizedToolStripMenuItem();
        Controls.LocalizedToolStripMenuItem toolFollow = new Controls.LocalizedToolStripMenuItem();
        Controls.LocalizedToolStripMenuItem toolConfGrabber = new Controls.LocalizedToolStripMenuItem();


        private void InitializeContextMenu()
        {
            contextMenu.Opening += new CancelEventHandler(contextMenu_Opening);

            toolSaveCurrentLog.ResourceName = "MENU_SAVE_LOG";
            toolCopyToClipboard.ResourceName = "MENU_COPY_TO_CLIP";
            toolClearAll.ResourceName = "MENU_CLEAR_ALL";
            toolClearBeforeLine.ResourceName = "MENU_CLEAR_BEFORE_LINE";
            toolViewLastLine.ResourceName = "MENU_VIEW_LAST_LINE";
            toolGrabberFilters.ResourceName = "MENU_MANAGE_GRABBER_FILTERS";
            toolDisplayFilters.ResourceName = "MENU_MANAGE_DISPLAY_FILTERS";
            toolFollow.ResourceName = "MENU_FOLLOW";
            toolConfGrabber.ResourceName = "MENU_CONFIGURE_GRABBER";

            _rtb.ContextMenuStrip = contextMenu;
            contextMenu.Items.Add(toolSaveCurrentLog);
            contextMenu.Items.Add(toolCopyToClipboard);
            contextMenu.Items.Add("-");
            contextMenu.Items.Add(toolClearAll);
            contextMenu.Items.Add(toolClearBeforeLine);
            contextMenu.Items.Add("-");
            contextMenu.Items.Add(toolConfGrabber);
            contextMenu.Items.Add("-");
            contextMenu.Items.Add(toolGrabberFilters);
            contextMenu.Items.Add(toolDisplayFilters);
            contextMenu.Items.Add("-");
            contextMenu.Items.Add(toolViewLastLine);
            contextMenu.Items.Add(toolFollow);

            toolViewLastLine.CheckState = CheckState.Checked;
            toolFollow.CheckState = CheckState.Checked;

            toolSaveCurrentLog.Click += new EventHandler(toolSaveCurrentLog_Click);
            toolCopyToClipboard.Click += new EventHandler(toolCopyToClipboard_Click);
            toolClearAll.Click += new EventHandler(toolClear_Click);
            toolClearBeforeLine.Click += new EventHandler(toolClearToHere_Click);
            toolViewLastLine.Click += new EventHandler(toolViewLastLine_Click);
            toolGrabberFilters.Click += new EventHandler(toolGrabberFilters_Click);
            toolDisplayFilters.Click += new EventHandler(toolDisplayFilters_Click);
            toolFollow.Click += new EventHandler(toolFollow_Click);
            toolConfGrabber.Click += new EventHandler(toolConfGrabber_Click);
        }


        private void contextMenu_Opening(object sender, CancelEventArgs e)
        {
            toolClearBeforeLine.Enabled = _rtb.LastRightClickedLineFirstCharIndex > 0;
        }

        #endregion


        public Viewer()
        {
            /// Rich text box initialization
            /// 
            _rtb = new ViewerRichTextBox();

            this.Controls.Add(_rtb);
            _displayFilters = new List<DisplayFilter>();
        }

        public void SetContainer(SingleViewer container)
        {
            _viewerContainer = container;
            InitializeContextMenu();
        }


        #region Public properties

        public new Font Font
        {
            get { return _rtb.Font; }
            set { _rtb.Font = value; }
        }
        public FontStyle DefaultFontStyle
        {
            get { return _rtb.Font.Style; }
            set { _rtb.Font = new Font(_rtb.Font, value); }
        }
        public new Color DefaultForeColor
        {
            get { return _rtb.ForeColor; }
            set { _rtb.ForeColor = value; }
        }
        public new Color DefaultBackColor
        {
            get { return _rtb.BackColor; }
            set { _rtb.BackColor = value; }
        }
        public new string Text
        {
            get { return _rtb.Text; }
            set { _rtb.Text = value; }
        }
        public RichTextBoxScrollBars ScrollBars
        {
            get { return _rtb.ScrollBars; }
            set { _rtb.ScrollBars = value; }
        }

        public bool Follow
        {
            get { return _follow; }
            set 
            {
                _follow = value;
                _rtb.FollowMode = _follow;
                if (_follow)
                {
                    toolFollow.CheckState = CheckState.Checked;
                    _rtb.Select(_rtb.Text.Length, 0);
                    _rtb.ScrollToCaret();
                }
                else
                {
                    toolFollow.CheckState = CheckState.Unchecked;
                }
            }
        }

        public List<DisplayFilter> DisplayFilters
        {
            get { return _displayFilters; }
        }

        public ContextMenuStrip DropDownMenu
        {
            get { return contextMenu; }
        }

        public int MaxVisibleLinesNumber
        {
            get { return _maxVisibleLinesNumber; }
            set { _maxVisibleLinesNumber = value; }
        }

        #endregion

        #region Public methods

        public void Clear()
        {
            _rtb.Text = "";
        }

        public delegate void AppendTextHandler(string s);
        public void AppendText(string s)
        {
            AppendText(s, false);
        }
        public void AppendText(string s, bool resetStyleBefore)
        {
            if (_disposing)
                return;

            if (string.IsNullOrEmpty(s))
                return;

            if (InvokeRequired)
            {
                BeginInvoke(new AppendTextHandler(AppendText), new object[] { s });
                return;
            }

            lock (_syncAppendingText)
            {
                _buffer += s;
                _buffer = _buffer.Replace("\r\n", "\n");
                int lastLFIdx = _buffer.LastIndexOf('\n');
                if (lastLFIdx < 0)
                    return;


                string fullRows = _buffer.Substring(0, lastLFIdx);
                string[] rows = fullRows.Split('\n');

                string stringToAppend = "";
                for (int i = 0; i < rows.Length; i++)
                {
                    string row = rows[i];
                    stringToAppend += row + "\r\n";
                }


                int originalSelStart = _rtb.SelectionStart;
                int originalSelLength = _rtb.SelectionLength;


                AppendTextAndStylize(stringToAppend, resetStyleBefore);

                
                if (lastLFIdx < (_buffer.Length - 1))
                    _buffer = _buffer.Substring(lastLFIdx + 1);
                else
                    _buffer = "";

                int removedChars = RemoveOldLines();

                if (_follow)
                {
                    _rtb.Select(_rtb.Text.Length, 0);
                    _rtb.ScrollToCaret();
                }
                else
                {
                    int selStart = originalSelStart - removedChars;
                    if (selStart < 0)
                        _rtb.DeselectAll();
                    else
                        _rtb.Select(originalSelStart - removedChars, originalSelLength);
                }
            }
        }

        public void SaveConfig(XmlNode nodeViewer)
        {
            XmlDocument xml = nodeViewer.OwnerDocument;

            nodeViewer.Attributes.Append(xml.CreateAttribute("follow"));
            nodeViewer.Attributes["follow"].Value = _follow.ToString();

            nodeViewer.Attributes.Append(xml.CreateAttribute("viewLastLine"));
            nodeViewer.Attributes["viewLastLine"].Value = _viewLastLine.ToString();

            XmlNode nodeFilters = xml.CreateElement("filters");
            nodeViewer.AppendChild(nodeFilters);
            foreach (DisplayFilter filter in _displayFilters)
            {
                XmlNode nodeFilter = xml.CreateElement("filter");
                filter.SaveConfig(nodeFilter);
                nodeFilters.AppendChild(nodeFilter);
            }
        }

        public void LoadConfig(XmlNode nodeViewer)
        {
            Follow = bool.Parse(nodeViewer.Attributes["follow"].Value);
            _viewLastLine = bool.Parse(nodeViewer.Attributes["viewLastLine"].Value);
            ToggleViewLastLine(false);

            XmlNode nodeFilters = nodeViewer.SelectSingleNode("filters");
            _displayFilters = new List<DisplayFilter>();
            foreach (XmlNode nodeFilter in nodeFilters.SelectNodes("filter"))
            {
                DisplayFilter filter = new DisplayFilter();
                filter.LoadConfig(nodeFilter);
                _displayFilters.Add(filter);
            }
        }

        /// <summary>
        /// Get the ViewLastLine status
        /// </summary>
        /// <param name="swap">specificy if swap state before get it</param>
        /// <returns>new ViewLastLine state</returns>
        public bool ToggleViewLastLine(bool swap)
        {
            if (swap)
                _viewLastLine = !_viewLastLine;

            toolViewLastLine.Checked = _viewLastLine;

            if (_viewerContainer != null)
            {
                _viewerContainer.LastLineVisible = _viewLastLine;
            }

            return _viewLastLine;
        }

        #endregion

        #region Private methods
     

        private void toolSaveCurrentLog_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog sfd = new SaveFileDialog())
            {
                sfd.CheckPathExists = true;
                sfd.DefaultExt = ".log";
                sfd.Filter = Globalization.ResourceManager.Current.GetString("DLG_LOG_FILE_DESC") + " (*.log)|*.log";
                sfd.OverwritePrompt = true;
                sfd.Title = Globalization.ResourceManager.Current.GetString("DLG_LOG_FILE_SAVE");
                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    if (File.Exists(sfd.FileName))
                        File.Delete(sfd.FileName);
                    using (StreamWriter sw = new StreamWriter(new FileStream(sfd.FileName, FileMode.CreateNew)))
                    {
                        sw.Write(_rtb.Text.Replace("\n","\r\n"));
                        sw.Flush();
                        sw.Close();
                    }
                }
            }
        }
        private void toolCopyToClipboard_Click(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(_rtb.Text);
        }
        private void toolGrabberFilters_Click(object sender, EventArgs e)
        {
            GrabberBase grabber = _viewerContainer.InnerGrabber;

            if (grabber == null)
                return;

            using (ManageFilters mngFrm = new ManageFilters(grabber.GrabFilters.ToArray()))
            {
                if (mngFrm.ShowDialog() == DialogResult.OK)
                {
                    List<GrabFilter> filters = grabber.GrabFilters;
                    filters.Clear();
                    foreach (FilterBase filter in mngFrm.ModifiedFilters)
                    {
                        filters.Add((GrabFilter)filter);
                    }
                }
            }
        }
        private void toolDisplayFilters_Click(object sender, EventArgs e)
        {
            using (ManageFilters mngFrm = new ManageFilters(this.DisplayFilters.ToArray()))
            {
                if (mngFrm.ShowDialog() == DialogResult.OK)
                {
                    _displayFilters.Clear();
                    foreach (FilterBase filter in mngFrm.ModifiedFilters)
                    {
                        _displayFilters.Add((DisplayFilter)filter);
                    }

                    AppendTextAndStylize(null, true);
                }
            }
        }
        private void toolFollow_Click(object sender, EventArgs e)
        {
            Follow = !Follow;
        }
        private void toolClear_Click(object sender, EventArgs e)
        {
            _rtb.Clear();
        }
        private void toolClearToHere_Click(object sender, EventArgs e)
        {
            _rtb.Select(0, _rtb.LastRightClickedLineFirstCharIndex);
            _rtb.SelectedText = "\0";
        }
        private void toolViewLastLine_Click(object sender, EventArgs e)
        {
            ToggleViewLastLine(true);
        }
        private void toolConfGrabber_Click(object sender, EventArgs e)
        {
            GrabberBase grabber = _viewerContainer.InnerGrabber;

            if (grabber == null)
                return;

            if (grabber.ShowConfigDialog() == DialogResult.OK)
            {
                grabber.RestartGrab();
                _viewerContainer.UpdateTitle();
            }
        }


        private void AppendTextAndStylize(string text, bool resetStyleBefore)
        {
            if (text == null) 
                text = string.Empty;


            int curSelStart = _rtb.SelectionStart;
            int curSelLength = _rtb.SelectionLength;
            
            bool rtbWasFocused = _rtb.Focused;
            if (rtbWasFocused)
            {
                _rtb.FollowMode = true;
            }


            int start = _rtb.Text.Length;
            _rtb.AppendText(text);

            if (!resetStyleBefore)
            {
                ApplyDisplayFilters(start);
            }
            else
            {
                ResetView();
                ApplyDisplayFilters(0);
            }

            _rtb.SelectionStart = curSelStart;
            _rtb.SelectionLength = curSelLength;

            if (rtbWasFocused)
            {    
                _rtb.FollowMode = false;
                _rtb.Focus();
            }
            
        }

        private int RemoveOldLines()
        {
            int lastLineNumber = _rtb.GetLineFromCharIndex(_rtb.Text.Length); // the 2 chars are \r\n
            int linesToRemoveCount = lastLineNumber - _maxVisibleLinesNumber;

            if (linesToRemoveCount <= 0)
                return 0;

            int lenToRemove = _rtb.GetFirstCharIndexFromLine(linesToRemoveCount);

            _rtb.Select(0, lenToRemove);
            _rtb.SelectedText = "\0";

            return lenToRemove;
        }

        private void ApplyDisplayFilters(int start)
        {
            if (start >= _rtb.Text.Length)
                return;

            foreach (DisplayFilter filter in _displayFilters)
            {
                if (filter.InnerRegex == null) continue;

                foreach(Match m in filter.InnerRegex.Matches(_rtb.Text, start))
                {
                    _rtb.Select(m.Index, m.Length);
                    _rtb.SelectionColor = filter.ForeColor;
                    _rtb.SelectionBackColor = filter.BackColor;
                    _rtb.SelectionFont = new Font(_rtb.Font, filter.FontStyle);
                }
            }

        }

        private void ResetView()
        {
            _rtb.SelectAll();
            _rtb.SelectionFont = _rtb.Font;
            _rtb.SelectionColor = _rtb.ForeColor;
            _rtb.SelectionBackColor = _rtb.BackColor;
            _rtb.DeselectAll();

        }
        

        #endregion

        #region Private classes

        private class LogLine
        {
            private int _startIndex = -1;
            private int _length = 0;

            public LogLine(int startIndex, int length)
            {
                _startIndex = startIndex;
                _length = length;
            }

            public int StartIndex
            {
                get { return _startIndex; }
            }
            public int Length
            {
                get { return _length; }
            }
        }

        #endregion

        #region Overrides

        protected override void Dispose(bool disposing)
        {
            _disposing = true;
            base.Dispose(disposing);
        }

        #endregion

    }
}